package pkg

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"github.com/redis/go-redis/v9"
)

var upgrader = websocket.Upgrader{
	WriteBufferSize: 1024,
	ReadBufferSize:  1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type Message struct {
	Event    string    `json:"event"`
	Id       string    `json:"id"`
	Date     time.Time `json:"date"`
	Username string    `json:"username"`
	Body     string    `json:"body"`
}

type Server struct {
	clients        map[*websocket.Conn]bool
	messageHandler func(msg Message) error
}

func StartServer(messageHandler func(msg Message) error, client *redis.Client, ctx context.Context) *Server {
	server := Server{
		make(map[*websocket.Conn]bool),
		messageHandler,
	}

	http.HandleFunc("/", server.chat)
	http.HandleFunc("/getPrevMessages", func(w http.ResponseWriter, r *http.Request) { getPrevMessages(w, r, client, ctx) })

	go http.ListenAndServe(":8080", nil)

	forever := make(chan bool)
	log.Printf("[*] Server has been started on port 8080. To exit press CTRL+C")
	<-forever
	return &server
}

func (s *Server) chat(w http.ResponseWriter, r *http.Request) {
	connection, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer connection.Close()

	s.clients[connection] = true
	defer delete(s.clients, connection)

	for {
		var msg Message
		err := connection.ReadJSON(&msg)
		if err != nil || msg.Event == "close" {
			log.Print(err)
			break
		}

		switch msg.Event {
		case "connection":
			s.broadcast(msg)
		case "message":
			s.broadcast(msg)
		case "disconnected":
			s.broadcast(msg)
		}

		go s.messageHandler(msg)
	}
}

func (s *Server) broadcast(msg Message) {
	for conn := range s.clients {
		conn.WriteJSON(msg)
	}
}

func getPrevMessages(w http.ResponseWriter, r *http.Request, client *redis.Client, ctx context.Context) map[string]string {
	keys := client.Do(ctx, "KEYS", "*")

	var data []map[string]string

	for _, key := range keys {
		data = append(data, client.HGetAll(ctx, key).Val())
	}

	return make(map[string]string)
}
